#pragma once
#include<string>
#include<vector>
#include<memory>

// ini 文件中每一行的类型
enum class IniLineType
{
	UNKNOWN_TPYE = 0,       // 未知类型
	SECTION =1,             // section
	KEY_VALUE_PAIR =2,      // 键值对 
	COMMENT = 3,            // 注释
	BLANK_LINE = 4          // 空行
};

class CIniLine;
using INI_ITEM_VECTOR = std::vector<std::shared_ptr<CIniLine>>;

// ini项
class CIniLine
{
public:
	// 构造空行和单行注释
	CIniLine(IniLineType type, std::string str);

	// 构造section
	CIniLine(IniLineType type, std::string sectionName, std::string comment);

	// 构造键值对
	CIniLine(IniLineType type, std::string key, std::string value, std::string comment);
	
	virtual ~CIniLine();

	virtual std::string ToString();

	IniLineType GetType() const { return m_type; }
	std::string GetComment() const { return m_comment; }
	std::string GetSectionName()  const { return m_stctionName; }
	std::string GetKey()  const { return m_key; }
	std::string GetValue()  const { return m_value; }

	void SetComment(std::string strComment) { m_comment = strComment; }
	void SetValue(std::string strValue) { m_value = strValue; }

	bool AddItem(std::shared_ptr<CIniLine>& pKeyValue);

	std::shared_ptr<CIniLine> GetKeyValuePair(const std::string& key);

	// 删除最后一个空行，如果结尾时空行则删除
	bool RemoveLastBlankLine();

	INI_ITEM_VECTOR& GetAllItem();

private:
	// 类型
	IniLineType m_type;
	// 注释
	std::string m_comment;

	// section 名称
	std::string m_stctionName;

	// 键
	std::string m_key;

	// 值
	std::string m_value;

	// section 下的键值对
	INI_ITEM_VECTOR m_vecKeyValue;
};


// ini文件解析类
class CIniParser
{
public:
	CIniParser();
	~CIniParser();

	// 构造函数 - 加载文件
	CIniParser(const std::string& file_name);

	// 加载一个ini文件, 如果之前的文件被修改, 那么之前的ini文件将会被保持。
	bool LoadIniFile(const std::string& fileName, std::string& str);

	// 从字符串中作为ini文件加载
	bool ParserFromString(const std::string& str);

	// 保持到加载ini位置
	bool Save();

	// 另存为一个和加载路径不一样的文件中
	bool SaveAs(const std::string& file_name);

	// 返回ini是否被修改, 或者他最后一次操作是保存
	bool IsModified() const { return m_modified; }

public:
	// 下面的成员函数是从Section中获得一些值
	long GetInteger(const std::string& section, const std::string& key, long def_val);
	float GetFloat(const std::string& section, const std::string& key, float def_val);
	long GetString(const std::string& section, const std::string& key, const std::string& def_val, std::string& dst_str);
	const std::string GetString(const std::string& section, const std::string& key, const std::string& def_val);

	void SetInteger(const std::string& section, const std::string& key, long value);
	void SetFloat(const std::string& section, const std::string& key, float value);
	void SetString(const std::string& section, const std::string& key, const std::string& value);

private:
	
	IniLineType GetLineType(char ch);

	// 解析行内的注释
	bool ParseInlineComment(const std::string& str, std::string& val, std::string& comment);

	std::shared_ptr<CIniLine> CreateSection(const std::string& section, const std::string& comment = "");

	std::shared_ptr<CIniLine> CreateNewKeyValue(std::shared_ptr<CIniLine>& pSection, const std::string& key, const std::string& val, const std::string& comment = "");

	// 根据section 名称查找section
	std::shared_ptr<CIniLine> GetSectionByName(const std::string& section);

	// 根据section名称和键名称获取键值对
	std::shared_ptr<CIniLine> GetKeyValuePair(const std::string& section, const std::string& key);

	// 跟根据section名称和键名称获值(字符串)
	std::string GetValue(const std::string& section, const std::string& key);

	// 加载之前先保存
	void SaveBeforeLoad();

	// 标记已经更新
	void MarkModified() { m_modified = true; }

	// 清除所有数据释放内存
	void ClearAllData();

private:
	// 用指定字符串来分割字符串
	static std::vector<std::string> SplitString(const  std::string& s, const std::string& delim);

	// 去除字符串左右两边的空格
	static std::string StringTrim(const std::string& str);

private:
	// 是否被修改
	bool m_modified; 

	//文件名(路径)
	std::string m_filePath; 

	// 按顺序存储ini文件每一行解析后的内容
	std::vector<std::shared_ptr<CIniLine>> m_vecItems;
};

